home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 February / EnigmA AMIGA RUN 15 (1997)(G.R. Edizioni)(IT)[!][issue 1997-02][PLANET CD V].iso / enigma / earcd / sviluppo / svilupp2 / amphn192.lha / src / codec.c < prev    next >
C/C++ Source or Header  |  1996-11-16  |  14KB  |  524 lines

  1. #ifndef CODEC_C
  2. #define CODEC_C
  3.  
  4. /* codec.c : routines shared by AmiPhone AND AmiPhoned--govern audio
  5.             compression, decompression, and playing */
  6.  
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <time.h>
  11.  
  12. #include <exec/types.h>
  13. #include <exec/memory.h>
  14. #include <exec/ports.h>
  15. #include <devices/audio.h>
  16. #include <libraries/dos.h>        /* contains RETURN_OK, RETURN_WARN #def's */
  17. #include <graphics/gfxbase.h>        /* to determine if we are on a PAL or NTSC Amiga */
  18.  
  19. #include <clib/intuition_protos.h>
  20. #include <clib/alib_protos.h>
  21. #include <clib/exec_protos.h>
  22. #include <clib/dos_protos.h>
  23. #include <clib/socket_protos.h>
  24.  
  25. #include <pragmas/socket_pragmas.h>
  26.  
  27. #ifndef AMIPHONE
  28. #include "AmiPhoned.h"
  29. #else
  30. #include "AmiPhone.h"
  31. #endif
  32.  
  33. #include "AmiPhoneMsg.h"
  34. #include "AmiPhonePacket.h"
  35. #include "compress/ADPCM.h"
  36. #include "codec.h"
  37. #include "TCPQueue.h"
  38.  
  39. char szLastMemoFile[200];
  40.  
  41. #ifndef CONV_UTIL
  42.  
  43. extern struct Library * SocketBase;
  44.  
  45. /* These all need to be defined elsewhere in the code for AllocAudio to work. */
  46. extern struct GfxBase * GfxBase;
  47. extern struct MsgPort *port1, *port2;
  48. extern struct IOAudio *AIOptr1, *AIOptr2;
  49. extern ULONG SystemClock;
  50. extern ULONG ulMilliSecondsTaken;
  51.  
  52. /* These go with AmiPhoned.h only */
  53. #ifndef AMIPHONE
  54. extern char * szMessageDir;
  55. extern ULONG ulByteTicker;
  56. extern FILE * fpDebug;
  57. extern LONG sRelayTCPSocket[];
  58. #endif
  59.  
  60. static ULONG ulBytesWritten = 0L;
  61.  
  62.  
  63.  
  64.  
  65. /* Read and collect bytes from the TCP stream.  If there
  66.    are enough to make a full packet, return a pointer to
  67.    the packet, otherwise return NULL. */
  68. struct AmiPhoneSendBuffer * GetTCPPacket(LONG sReceiveTCPSocket)
  69. {
  70.     static UBYTE ubData[sizeof(struct AmiPhoneSendBuffer)];
  71.     static UBYTE * pubNext    = ubData;
  72.     static int nLeftToRead    = sizeof(struct AmiPhonePacketHeader);
  73.     static BOOL BGotHeader    = FALSE;
  74.     int nBytesRead;
  75. #ifndef AMIPHONE
  76.     int i;
  77. #endif
  78.  
  79.     /* If we're already good to go, don't read any more! */
  80.     if (nLeftToRead == 0) 
  81.     {
  82.         BGotHeader  = FALSE;
  83.         nLeftToRead = sizeof(struct AmiPhonePacketHeader);
  84.         pubNext     = ubData;
  85.         return(ubData);
  86.     }
  87.     
  88.     /* Else grab as much as we can from the stream */
  89.     nBytesRead = recv(sReceiveTCPSocket, pubNext, nLeftToRead, 0L);
  90.     if (nBytesRead <= 0) return(NULL);  /* error == no data */
  91.  
  92. #ifndef AMIPHONE
  93.     /* If we have any relay children, forward the data to them too. */
  94.     for (i=0;i<10;i++)
  95.         if (sRelayTCPSocket[i] != -1) AttemptTCPSend(sRelayTCPSocket[i], i, pubNext, nBytesRead);
  96. #endif
  97.  
  98.     if (nBytesRead == nLeftToRead)
  99.     {
  100.         /* Okay, we got either the full header or the full data! */
  101.         if (BGotHeader)
  102.         {
  103.             /* We already had the header, so now we must have the data too */
  104.             /* Restart for the next time, and return the data */
  105.             BGotHeader  = FALSE;
  106.             nLeftToRead = sizeof(struct AmiPhonePacketHeader);
  107.             pubNext     = ubData;
  108.             
  109.             return(ubData);
  110.         }
  111.         else
  112.         {
  113.             struct AmiPhonePacketHeader * pHead = (struct AmiPhonePacketHeader *) ubData;
  114.             
  115.             /* Okay, we just got the header.  Now setup to get the data. */
  116.             BGotHeader  = TRUE;
  117.             nLeftToRead = (int) pHead->ulDataLen;
  118.             pubNext = ubData + sizeof(struct AmiPhonePacketHeader);
  119.             
  120.             if (nLeftToRead > (sizeof(ubData)-sizeof(struct AmiPhonePacketHeader)))
  121.             {
  122.                 nLeftToRead = sizeof(ubData)-sizeof(struct AmiPhonePacketHeader);
  123.             }
  124.                         
  125.             /* Now try to read the rest if we can */
  126.             return(GetTCPPacket(sReceiveTCPSocket));
  127.         }
  128.     }
  129.     else
  130.     {
  131.         /* Got >0 bytes, but not all we needed.  Mark our place for next time. */
  132.         nLeftToRead -= nBytesRead;
  133.         pubNext     += nBytesRead;
  134.         return(NULL);
  135.     }
  136. }
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144. /* Give TRUE, allocates the audio hardware and inits info.  Give, FALSE,
  145.    cleans up. */  
  146. BOOL AllocAudio(BOOL BAlloc)
  147. {
  148.     int c;
  149.     static ULONG device = 1L;
  150.     static UBYTE chan1[]  = { 1 };        /* Audio channel allocation arrays */
  151.     static UBYTE chan2[]  = { 2 };
  152.     static UBYTE chan3[]  = { 4 };
  153.     static UBYTE chan4[]  = { 8 };
  154.     static UBYTE *chans[] = {chan1, chan2, chan3, chan4};
  155.     
  156.     if (BAlloc == TRUE)
  157.     {
  158.         /* ALLOCATE AUDIO HARDWARE */
  159.     
  160.         /* get PAL/NTSC clock constant set */
  161.         if (GfxBase->DisplayFlags & PAL) SystemClock = 3546895L;    /* PAL */
  162.                             else SystemClock = 3579545L;    /* NTSC */
  163.  
  164.         /* Make audio messages ?? */
  165.         UNLESS (AIOptr1 = (struct IOAudio *) AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) return(FALSE);
  166.         UNLESS (AIOptr2 = (struct IOAudio *) AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) return(FALSE);
  167.  
  168.         /* Create our audio ports */
  169.         UNLESS (port1 = CreatePort(0,0)) return(FALSE);
  170.         UNLESS (port2 = CreatePort(0,0)) return(FALSE);
  171.  
  172.         /* Grab the first free audio channel--OpenDevice returns 0 on success */
  173.         c=0;
  174.         while ((device)&&(c<4))
  175.         {
  176.             AIOptr1->ioa_Request.io_Message.mn_ReplyPort   = port1;
  177.             AIOptr1->ioa_Request.io_Message.mn_Node.ln_Pri = 127;    /* Don't let anyone take it from us! */
  178.             AIOptr1->ioa_AllocKey                   = 0;
  179.             AIOptr1->ioa_Data                   = chans[c];
  180.             AIOptr1->ioa_Length                   = 1;        /* Size of the allocation array--1 channel => 1 */
  181.  
  182.             device = OpenDevice(AUDIONAME, 0L, (struct IORequest *) AIOptr1, 0L);
  183.             c++;
  184.         }
  185.     
  186.         /* device != 0 then we never got a channel */
  187.         if (device) return(FALSE);
  188.  
  189.         
  190.         /* Set up Audio IO Blocks for Sample Playing */
  191.         AIOptr1->ioa_Request.io_Command    = CMD_WRITE;
  192.         AIOptr1->ioa_Request.io_Flags    = ADIOF_PERVOL;
  193.         
  194.         /* High volume */
  195.         AIOptr1->ioa_Volume = 60;
  196.     
  197.         /* Set cycles */
  198.         AIOptr1->ioa_Cycles = 1;
  199.     
  200.         *AIOptr2 = *AIOptr1;    /* Make sure we have the same allocation keys */
  201.                     /* same channels selected and same flags (but */
  202.                     /* different ports...) */
  203.  
  204.         /* Here's where we differentiate--which signal the reply will buzz */
  205.         AIOptr1->ioa_Request.io_Message.mn_ReplyPort = port1;
  206.         AIOptr2->ioa_Request.io_Message.mn_ReplyPort = port2;
  207.     
  208.         /* THESE pointers point to the beginning of your samples! */
  209.         AIOptr1->ioa_Data = NULL;
  210.         AIOptr2->ioa_Data = NULL;
  211.     
  212.         /* These values are the length of the samples! */
  213.         AIOptr1->ioa_Length = 0L;
  214.         AIOptr2->ioa_Length = 0L;
  215.     }
  216.     else
  217.     {
  218.         /* FREE AUDIO HARDWARE */
  219.         if (device == 0) {CloseDevice((struct IORequest *)AIOptr1); device = 1;} 
  220.         if (port1)     {DeletePort(port1); port1 = NULL;}
  221.         if (port2)     {DeletePort(port2); port2 = NULL;}
  222.         if (AIOptr1)     {FreeMem(AIOptr1, sizeof(struct IOAudio)); AIOptr1 = NULL;}
  223.         if (AIOptr2)     {FreeMem(AIOptr2, sizeof(struct IOAudio)); AIOptr2 = NULL;}
  224.     }
  225.     return(TRUE);
  226. };
  227.  
  228.  
  229. #endif    /* CONV_UTIL */
  230.  
  231. /* Only use this if we're compiling for the client! */
  232. #ifdef AMIPHONE
  233. ULONG CompressData(UBYTE * ubIn, UBYTE * ubOut, UBYTE bCompType, ULONG ulInBytes, ULONG * pulUpdateJoinCode)
  234. {
  235.     switch(bCompType)
  236.     {
  237.         case COMPRESS_NONE:
  238.             memcpy(ubOut,ubIn,ulInBytes);
  239.             return(ulInBytes);
  240.             break;
  241.             
  242.         case COMPRESS_ADPCM2:
  243.             *pulUpdateJoinCode = CompressADPCM2(ubIn, ulInBytes, ubOut, *pulUpdateJoinCode);
  244.             return((ulInBytes+3)/4);
  245.             break;
  246.  
  247.         case COMPRESS_ADPCM3:
  248.             *pulUpdateJoinCode = CompressADPCM3(ubIn, ulInBytes, ubOut, *pulUpdateJoinCode);
  249.             return((ulInBytes+7)/8*3);
  250.             break;
  251.     
  252.         default:
  253.             printf("Unknown compression method %i.\n",bCompType);
  254.             return(1L);
  255.             break;
  256.     }
  257. }
  258. #endif
  259.  
  260. /* Returns the number of bytes decompressed to ubOut */
  261. ULONG DecompressData(UBYTE * ubIn, UBYTE * ubOut, UBYTE ubCompMode, ULONG ulLen, ULONG ulJoinCode)
  262. {
  263.     switch(ubCompMode)
  264.     {
  265.         case COMPRESS_NONE:
  266.             memcpy(ubOut, ubIn, ulLen);
  267.             return(ulLen);
  268.             break;
  269.  
  270.         case COMPRESS_ADPCM2: 
  271.             (void) DecompressADPCM2(ubIn, ulLen, ubOut, ulJoinCode);
  272.             return(ulLen*4);        
  273.             break;  
  274.             
  275.         case COMPRESS_ADPCM3: 
  276.             (void) DecompressADPCM3(ubIn, ulLen, ubOut, ulJoinCode);
  277.             return(ulLen*8/3);
  278.             break; 
  279.             
  280.         default:    
  281.             #ifndef AMIPHONE
  282.              #ifdef DEBUG_FLAG
  283.               fprintf(fpDebug,"DecompressData: unknown mode %i\n",ubCompMode);
  284.              #endif
  285.             #endif
  286.             return(0);
  287.     }
  288. }
  289.  
  290.  
  291. #ifndef CONV_UTIL
  292.  
  293. /* Returns TRUE if a VWARN packet should be sent, FALSE if not. */
  294. BOOL CheckVersions(char * szRemoteSoftwareName, ULONG ulRemoteVersionNumber, BOOL BOkToShowReq)
  295. {
  296.     char szBuf[500];
  297.     ULONG ulRemoteMajor = ulRemoteVersionNumber/10;
  298.     ULONG ulLocalMajor  = VERSION_NUMBER/10;
  299.     
  300.     if (ulRemoteMajor == ulLocalMajor) 
  301.     {
  302.         return(FALSE);
  303.     }
  304.     else if ((ulRemoteMajor < 17)||(ulRemoteMajor > 100))
  305.         {
  306.              sprintf(szBuf,"Warning:  The %s you are\nconnected to is using a pre-v1.7\nversion of AmiPhone.  To minimize\ncompatibility problems, you should\ntell your party to upgrade to the\nlatest version of AmiPhone.",
  307.                       szRemoteSoftwareName);
  308.     }
  309.     else if (ulRemoteMajor < ulLocalMajor) 
  310.     {
  311.         return(TRUE);
  312.     }
  313.     else sprintf(szBuf,"Warning:  The %s you are\nconnected to is using AmiPhone\nversion %i.%i.  To minimize compatibility\nproblems, you should upgrade your\nAmiPhone software to that version.",szRemoteSoftwareName,ulRemoteVersionNumber/100,ulRemoteVersionNumber%100);
  314.     
  315.     if (BOkToShowReq) 
  316.     {
  317.         #ifdef AMIPHONE
  318.         MakeReq("AmiPhone Version Warning", szBuf, NULL);
  319.         #else
  320.         DMakeReq("AmiPhoned Version Warning", szBuf, NULL);
  321.         #endif
  322.     }
  323.     return(FALSE);
  324. }
  325.  
  326.  
  327. void PrintPacketHeader(FILE * fpOut, struct AmiPhonePacketHeader * header)
  328. {
  329.     fprintf(fpOut,"Command = %c\n",header->ubCommand);
  330.     fprintf(fpOut,"Type    = %i\n",header->ubType);
  331.     fprintf(fpOut,"Seqnum  = %d\n",header->lSeqNum);
  332.     fprintf(fpOut,"Dta/BPS = %d\n",header->ulBPS);
  333.     fprintf(fpOut,"DataLen = %d\n",header->ulDataLen);    
  334.     fprintf(fpOut,"JoinCode= %d\n",header->ulJoinCode);    
  335.     fflush(fpOut);
  336. }
  337.  
  338.  
  339. FILE * OpenMessageFile(time_t tTime, char * szMessageDir)
  340. {
  341.     char szFileName[500];
  342.     
  343.     /* generate filename */
  344.     UNLESS(GetMessageFileName(tTime, szMessageDir, szFileName,sizeof(szFileName))) return(NULL);
  345.     Strncpy(szLastMemoFile, szFileName, sizeof(szLastMemoFile));
  346.     return(fopen(szFileName,"ab"));
  347. }
  348.  
  349. void RemoveMessageFile(time_t tTime, char * szMessageDir)
  350. {
  351.     char szFileName[500];
  352.     
  353.     /* generate filename */
  354.     UNLESS(GetMessageFileName(tTime, szMessageDir, szFileName,sizeof(szFileName))) return;
  355.     
  356.     remove(szFileName);
  357. }
  358.  
  359.  
  360. /* Generate a succinct timestamp */
  361. char * TimeStamp(time_t * optTime)
  362. {
  363.     static char szReturn[36];
  364.     const char * szDays[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  365.     struct tm * time_tm = localtime(optTime);
  366.     
  367.     sprintf(szReturn,"%02d:%02d:%02d on %s %02d/%02d/%02d",
  368.         time_tm->tm_hour,
  369.         time_tm->tm_min,
  370.         time_tm->tm_sec,
  371.         szDays[time_tm->tm_wday],
  372.         time_tm->tm_mon+1,
  373.         time_tm->tm_mday,
  374.         time_tm->tm_year);
  375.         
  376.     return(szReturn);
  377. }
  378.  
  379. /* resets the bytes written counter.  Returns TRUE if
  380.    the counter was non-zero, else FALSE */
  381. BOOL ResetByteCounter(void)
  382. {
  383.     BOOL BRet = (ulBytesWritten > 0);
  384.  
  385.     ulBytesWritten = 0L;    
  386.     return(BRet);
  387. }
  388.  
  389. /* save the given packet to file fpOutFile. */
  390. void SavePacket(struct AmiPhonePacketHeader * packet, FILE * fpOutFile)
  391. {
  392.     static char ulMagicWord[] = "APHN";
  393.     ULONG ulPacketSize;
  394.  
  395. #ifndef AMIPHONE
  396.     ULONG ulDiskBytesFree = FreeSpaceOnDisk(szMessageDir) * 1024;
  397.     
  398.     /* cut us down if the disk free gets lower than our count makes it
  399.        out to be... this could happen if someone else saves a file for instance */
  400.     if (ulByteTicker >= ulDiskBytesFree) 
  401.     {
  402.         #ifdef DEBUG_FLAG
  403.         fprintf(fpDebug,"SavePacket: Uh-oh, only %i kbytes of disk space left!\n",ulDiskBytesFree);
  404.         fflush(fpDebug);
  405.         #endif
  406.         
  407.         ulByteTicker = ulDiskBytesFree;
  408.     }
  409. #endif
  410.  
  411.     if ((packet == NULL)||(fpOutFile == NULL)) 
  412.     {
  413.         #ifndef AMIPHONE
  414.         EXIT("Couldn't write packet to disk",RETURN_OK)
  415.         #else
  416.         return;
  417.         #endif
  418.     }
  419.  
  420.     ulPacketSize = packet->ulDataLen + sizeof(struct AmiPhonePacketHeader);
  421.  
  422. #ifndef AMIPHONE
  423.     /* are we full up? */
  424.     if (ulByteTicker < ulPacketSize) EXIT("Message length limit reached or Disk full",RETURN_OK)
  425. #endif
  426.  
  427.     /* Write header? */
  428.     if (ulBytesWritten == 0L) 
  429.     {
  430.         #ifndef AMIPHONE
  431.         ulByteTicker -= 
  432.         #endif
  433.         fwrite(ulMagicWord,4,1,fpOutFile);
  434.     }
  435.     if (fwrite(packet, 1, ulPacketSize, fpOutFile) < ulPacketSize) 
  436.     {
  437.         #ifndef AMIPHONE
  438.         EXIT("Couldn't write packet to disk", RETURN_OK)
  439.         #else
  440.         printf("SavePacket:  Error, couldn't write packet to disk.\n");
  441.         return;
  442.         #endif
  443.     }
  444.  
  445.     ulBytesWritten += ulPacketSize;
  446.     ulMilliSecondsTaken += MilliSecondDuration(packet);
  447. #ifndef AMIPHONE
  448.     ulByteTicker -= ulPacketSize;
  449. #endif
  450. }
  451.  
  452.  
  453. /* returns the duration, in milliseconds, that the packet will play for */
  454. ULONG MilliSecondDuration(struct AmiPhonePacketHeader * packet)
  455. {
  456.     ULONG ulBytesOfSound = packet->ulDataLen;
  457.  
  458.     /* Now multiply by 1000 so we get milliseconds below*/
  459.     ulBytesOfSound *= 1000;
  460.         
  461.     /* Figure how many bytes we'll have when uncompressed */
  462.     switch(packet->ubType)
  463.     {
  464.         case COMPRESS_ADPCM2: ulBytesOfSound *= 4;     break;
  465.         case COMPRESS_ADPCM3: ulBytesOfSound *= (8/3); break;
  466.     }
  467.     
  468.     /* Now divide by the bytes per second to get the millseconds */
  469.     ulBytesOfSound /= (packet->ulBPS ? packet->ulBPS : 1);
  470.     
  471.     return(ulBytesOfSound);
  472. }
  473.  
  474.  
  475. /* set a file note if we can. Note that the file must already have
  476.    been closed...  */
  477. void SetMessageNote(time_t tStartTime, char * szFrom, char * szMessageDir, ULONG ulSecondsTaken)
  478. {
  479.     char szBuf[500], szBuf2[120];
  480.         
  481.     if (GetMessageFileName(tStartTime, szMessageDir, szBuf, sizeof(szBuf)))
  482.     {
  483.         sprintf(szBuf2,"N %-25.25s%3u sec%s at %s", 
  484.             szFrom, ulSecondsTaken, (ulSecondsTaken == 1) ? " " : "s", TimeStamp(&tStartTime));
  485.         SetComment(szBuf, szBuf2);
  486.     }
  487. }
  488.  
  489.  
  490. /* Copies the file to save to into szBuf, and returns TRUE on success,
  491.    FALSE on failure. */
  492. BOOL GetMessageFileName(time_t tTime, char * szMessageDir, char *szBuf, int nBufLength)
  493. {
  494.     char szBuf2[50];
  495.  
  496.     if (szMessageDir == NULL) return(FALSE);
  497.  
  498.     /* start with our path */
  499.     strncpy(szBuf, szMessageDir, nBufLength);
  500.     
  501.     /* and we'll need a filename */
  502.     sprintf(szBuf2, "AmiPhoneMessage.%u", tTime);
  503.     
  504.     /* Add them together... */
  505.     UNLESS(AddPart(szBuf, szBuf2, nBufLength)) return(FALSE);
  506.  
  507.     return(TRUE);
  508. }
  509.  
  510.  
  511. #endif /* CONV_UTIL */
  512.  
  513. /* Just like strncpy, only it will guarantee that the string
  514.    will be terminated */
  515. char * Strncpy(char * s1, char * s2, int n)
  516. {
  517.     strncpy(s1,s2,n);
  518.     s1[n-1]='\0';
  519.     return(s1);
  520. }
  521.  
  522.  
  523. #endif
  524.